/*
 * =====================================================================================
 *       Filename:  config.c
 *    Description:  配置相关代码
 *        Created:  2015-07-04 12:52
 *         Author:  mien, m@luym.cn
 * =====================================================================================
 */
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <fcntl.h>
#include "config.h"

#include "dict.h"
#include "zipmap.h"

#define MAX_LINE_SIZE 1024

int _loaded = 0;
char _user[32];
char _group[32];
char _server[MAX_LINE_SIZE];
char _headers[MAX_LINE_SIZE];
char _cmdpath[PATH_MAX];

dictTable *_project, *_mimes;

/* 合并路径与文件名 (没有检查缓冲区边界) */
static char* abspath(const char *filename)
{
    static char path[PATH_MAX];
    const char *p = filename;
    if (p[0] == '/') {
        strcpy(path, filename);
    } else {
        if (p[0] == '.' && p[1] == '/') {
            p += 2;
        }
        strcpy(path, _cmdpath);
        strcat(path, p);
    }
    return path;
}

static void _setProjectField(const char *line, const char *key, char *value, size_t size)
{
    char *p;
    p = strstr(line, key);
    if (p != NULL) {
        p += strlen(key);
        if (*p != ':') return;
        p++;
        int i = 1;
        int len = strlen(line);
        char *pv = value;
        while(i < size && p < line + len) {
            if (*p == ';') break;
            *pv++ = *p++;
            i++;
        }
        *pv = '\0';
    }
}

static xsyncProject* _parseProject(const char *name, const char *line)
{
    xsyncProject *project;
    project = malloc(sizeof(xsyncProject));
    strcpy(project->name, name);
    
    _setProjectField(line, "auth", project->auth, sizeof(project->auth));
    _setProjectField(line, "path", project->path, sizeof(project->path));
    
    return project;
}

static void _loadMimeTypes(const char *mimefile)
{
    char buf[1024];
    char key[32];
    char val[256];

    FILE *f = fopen(abspath(mimefile), "r");
    if (f == NULL) return;

    while (fgets(buf, 1024, f)) {
        char *p = NULL;
        int k = 0;
        while (buf[k] == ' ') k++;
        if (buf[k] == '\n') continue;
        p = buf + k;
        while(buf[k] != ' ') k++;
        strncpy(val, p, buf + k - p);
        val[buf + k - p] = '\0';

        while (1) { 
            while (buf[k] == ' ') k++;
            p = buf + k;
            while (buf[k] != ' ' && buf[k] != '\n') k++;
            strncpy(key, p, buf + k - p);
            key[buf + k - p] = '\0';
            dictSet(_mimes, key, val);
            if (buf[k] == '\n') break;
        }
    }
    fclose(f);
}

static void _loadHeaders(const char *headersfile)
{
    char buf[1024];
    char* h = _headers;

    FILE *f = fopen(abspath(headersfile), "r");
    if (f == NULL) return;

    while (fgets(buf, 1024, f)) {
        int l, i = 0;
        while (1) {
            if (buf[i] == '\0' || buf[i] == '\n' || buf[i] == '\r' || i >= 1023) {
                buf[i] = '\0';
                break;
            }
            i++;
        }

        if (buf[0] == '\0' || buf[0] == '#' || buf[0] == ' ') {
            continue;
        }

        l = strlen(buf);

        if ((l + 3) + (h - _headers) > MAX_LINE_SIZE) {
            break;
        }

        strcpy(h, buf);
        h += strlen(buf);
        strcpy(h, "\r\n");
        h += 2;
    }

    *h = '\0';
    fclose(f);
}

void loadConfig(const char *program, const char *conf)
{
    if (_loaded) return;
    
    char buf[MAX_LINE_SIZE];
    char line[MAX_LINE_SIZE];
    char section[32];

    /* 初始配置 */
    zipInit(_server, sizeof(_server)); 
    _project = dictCreate(&dictTypeStringKeyFreeVal);
    _mimes   = dictCreateWithSize(&dictTypeStringKeyVal, 128);
 
    /* 执行程序 xsync 所在的路径 */
    realpath(program, _cmdpath);
    char *s = strrchr(_cmdpath, '/');
    if (s != NULL) {
        *(++s) = 0;
    }

    /* 读取并解析配置文件 */
    FILE *fp = fopen(abspath(conf), "r");
    if (fp == NULL) return;
    
    while (fgets(buf, MAX_LINE_SIZE, fp) != NULL) {
        /* 复制buf中的行字符到line中，去掉空格 */
        int i, j, flag, len;
        j = 0;
        flag = -1;
        for (i = 0; i < MAX_LINE_SIZE; i++) {
            if (buf[i] == ' ') continue;
            if (buf[i] == '\n' || buf[i] == '\r' || buf[i] == '\0') {
                line[j++] = '\0';
                break;
            }
            if (buf[i] == '=')
                flag = j;
            line[j++] = buf[i];
        }
        
        /* 忽略空行、注释行 */
        if (j < 2 || line[0] == '#') continue;
        
        /* 忽略格式错误的小节标记行 */
        if ((line[0] == '[' && line[j - 2] != ']') || (line[0] != '[' && line[j - 2] == ']'))
            continue; 
        
        /* 忽略格式错误的设置项行 */
        if (line[0] != '[' && flag <= 0) continue;
        
        /* 是小节 */
        if (line[0] == '[') {
            strncpy(section, line + 1, j - 3);
            section[j - 3] = '\0';
        }
        /* 是设置项 */
        else {
            line[flag] = '\0';
            if (!strcmp(section, "server")) {
                zipAdd(_server, line, line + flag + 1);
            }
            else if(!strcmp(section, "project")) {
                xsyncProject *pro = _parseProject(line, line + flag + 1); 
                dictSet(_project, line, pro);
                /* 是否默认项目 */
                char *p = strstr(line + flag + 1, "default");
                if ((p != NULL) && 
                    (p == line + flag + 1 || *(p - 1) == ' ' || *(p - 1) == ';') &&
                    (*(p + 7) == ';' || *(p + 7) == ' ' || *(p + 7) == '\0')
                ) {
                    zipAdd(_server, "default_project", line);
                }
            } /* project */
        }
    }

    fclose(fp);
    
    /* mime 类型 */
    char* mimefile = zipGet(_server, "mime");
    if (strlen(mimefile) > 0) {
        _loadMimeTypes(mimefile);
    }
    
    /* 自定义回应的 headers 设置 */
    char* headersfile = zipGet(_server, "headers");
    if (strlen(headersfile) > 0) {
        _loadHeaders(headersfile);
    }

    _loaded = 1;
}

void freeConfig()
{
    dictFree(_project);
    dictFree(_mimes);
}

void setUidGid()
{
    char* user = zipGet(_server, "user");
    char* group = zipGet(_server, "group");

    if (user != NULL && user[0] != '\0') {
        struct passwd *pwd;
        pwd = getpwnam(user);
        if (pwd != NULL)
            setuid(pwd->pw_uid);
    }
    if (group != NULL && group[0] != '\0') {
        struct group *grp;
        grp = getgrnam(_group);
        if (grp != NULL)
            setgid(grp->gr_gid);
    }
}

char *getServerConfig(char *key)
{
    return zipGet(_server, key);
}

xsyncProject *getProjectConfig(char *name)
{
    return dictGet(_project, name);
}

char *getMimesConfig(char *ext)
{
    return dictGet(_mimes, ext);
}

char *getHeadersConfig()
{
    if (_headers[0] != '\0') {
        return _headers;
    }
    return NULL;
}
